package no.sachmuld.methodcombination.impl.validate;

import no.sachmuld.methodcombination.api.BehaviourMethod;
import no.sachmuld.methodcombination.api.reflection.component.BehaviourMethodValidator;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 */
public class BehaviourMethodValidatorImpl implements BehaviourMethodValidator {

    private Class<?> behaviourClass;
    private Class<?> targetClass;
    private List<MethodCombinationValidationError> errors;
    private Set<Method> behaviourClassMethodsWithCorrespondingTargetMethod = new HashSet<>();

    public BehaviourMethodValidatorImpl(Class<?> behaviourClass, Class<?> targetClass, List<MethodCombinationValidationError> errors) {
        this.behaviourClass = behaviourClass;
        this.targetClass = targetClass;
        this.errors = errors;
    }

    @Override
    public void validate(BehaviourMethod behaviourMethod) {

        if (behaviourMethod.exists()) {
            behaviourClassMethodsWithCorrespondingTargetMethod.add(behaviourMethod.getMethod());

            if (Modifier.isStatic(behaviourMethod.getMethod().getModifiers())) {
                errors.add(new BehaviourMethodIsStatic(targetClass, behaviourMethod));
            }

            if (!behaviourMethod.hasValidReturnType()) {
                errors.add(new IncompatibleReturnType(targetClass, behaviourMethod));
            }

            if (!behaviourMethod.hasValidExceptionTypes()) {
                errors.add(new IncompatibleExceptionTypes(targetClass, behaviourMethod));
            }

        } else if (behaviourMethod.isRequired()) {
            errors.add(new MissingBehaviourMethod(behaviourMethod));
        }
    }

    public void atEnd() {
        for (Method method : behaviourClass.getDeclaredMethods()) {
            if (!behaviourClassMethodsWithCorrespondingTargetMethod.contains(method)) {
                errors.add(new ObsoleteBehaviourMethod(targetClass, behaviourClass, method.getName(), method.getParameterTypes()));
            }
        }

    }
}
